package com.paradogs.common.utils;

import com.baidu.bjf.remoting.protobuf.Any;
import com.baidu.bjf.remoting.protobuf.Codec;
import com.baidu.bjf.remoting.protobuf.ProtobufProxy;
import lombok.extern.slf4j.Slf4j;

import java.io.IOException;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * @author: yumi
 * @date: 2023/8/1  13:48
 * @Description: JProtobuf 编解码工具
 * !!! 子类不能转父类，如：NumMap 不能等于 Map，无法转换
 */
@Slf4j
public class ProtoUtils {

    /**
     * 优化效率，避免每次都创建一个编解码器，享元模式？
     * !! jprotobuf encode() 源码内部自带了缓存，所以其实不需要自己缓存了，
     * 可以直接 ProtobufProxy.create(clz).encode()
     */
    public static Map<Class<?>, Codec> codecMap = new ConcurrentHashMap<>();

    /** 编码 */
    public static <T> byte[] encode(T obj) {

        if (obj == null) {
            return new byte[0];
        }

        if (obj instanceof byte[]) {
            return (byte[]) obj;
        }

//        Codec<T> codec = (Codec<T>) ProtobufProxy.create(obj.getClass(), false, null, new JdkCompiler(obj.getClass().getClassLoader()), new TemplateCodeGenerator(obj.getClass()));
        Codec<T> codec = (Codec<T>) ProtobufProxy.create(obj.getClass());
        try {
            return codec.encode(obj);
        } catch (Exception e) {
            log.error("ProtoBuf 解析错误：{}", e.getMessage(), e);
        }
        return new byte[0];
    }

    /** 解码 */
    public static <T> T decode(byte[] bytes, Class<T> clz) {

        if (byte[].class.isAssignableFrom(clz)) {
            return (T) bytes;
        }
//        Codec<T> codec = (Codec<T>) ProtobufProxy.create(clz, false, null, new JdkCompiler(clz.getClassLoader()), new TemplateCodeGenerator(clz));
////        Codec<T> codec = ProtobufProxy.create(clz, false, null, new JdkCompiler(clz.getClassLoader()), new NumMapCodeGenerator(clz));
        Codec<T> codec = ProtobufProxy.create(clz);
        try {
            return codec.decode(bytes);
        } catch (IOException e) {
            e.printStackTrace();
        }
        return null;
    }

    /**
     * 解码 any 类型
     * 注意这里用的是 jprotobuf 的 Any 类， any 类型其实就是一个对象 (消息类型 + bytes消息内容)，自己也可以实现
     */
    public static <T> Any packAny(T data) {
        try {
            return Any.pack(data);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
    }

}
